package com.example.spider.util;

import cn.hutool.core.io.FileUtil;
import jakarta.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
import org.shoulder.core.context.AppInfo;

import java.io.File;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 定时全量刷新，适合少量数据无数据库存储
 * new LZ4Codec()
 *
 * @deprecated use {@link FileBasedCollection}
 */
@Slf4j
public class FileBasedSet<T> implements Set<T> {

    private String name = "fileSet";

    /**
     * 一般重试很多次肯定不行
     */
    private final Set<T> set;

    private final File storageFile;

    private final AtomicBoolean dirty = new AtomicBoolean(true);

    private final ReentrantLock loadLock = new ReentrantLock();

    public FileBasedSet(Set<T> set, File storageFile) {
        this.set = set;
        this.storageFile = storageFile;
    }

    public synchronized void loadFromStorage() {
        loadLock.lock();
        try {
            if (storageFile.exists()) {
                List<String> list = FileUtil.readLines(storageFile, AppInfo.charset());
                Set<T> loaded = list.stream().map(this::parseObject).collect(Collectors.toSet());
                set.addAll(loaded);
            }
        } finally {
            loadLock.unlock();
        }
    }

    public void syncToPersistenceStorage() {
        if (dirty.get()) {
            synchronized (this) {
                if (!dirty.get()) {
                    return;
                }
                dirty.set(false);
            }
            FileUtil.writeLines(set, storageFile, AppInfo.charset());
            log.info("sync memorySet({}) to storage: {}.", name, storageFile);
        }
    }

    protected String toStr(T obj) {
        return (String) obj;
    }

    protected T parseObject(String line) {
        return (T) line;
    }

    // =======================================================

    @Override public int size() {
        return set.size();
    }

    @Override public boolean isEmpty() {
        return set.isEmpty();
    }

    @Override public boolean contains(Object o) {
        return set.contains(o);
    }

    @Nonnull
    @Override
    public Iterator<T> iterator() {
        return set.iterator();
    }

    @Nonnull
    @Override
    public Object[] toArray() {
        return set.toArray();
    }

    @Nonnull
    @Override
    public <T1> T1[] toArray(@Nonnull T1[] a) {
        return set.toArray(a);
    }

    @Override public boolean add(T t) {
        boolean added = set.add(t);
        if (added) {
            dirty.set(true);
        }
        return added;
    }

    @Override public boolean remove(Object o) {
        boolean contains = set.remove(o);
        if (contains) {
            dirty.set(true);
        }
        return contains;
    }

    @Override
    public boolean containsAll(@Nonnull Collection<?> c) {
        return set.containsAll(c);
    }

    @Override
    public boolean addAll(@Nonnull Collection<? extends T> c) {
        boolean addAny = set.addAll(c);
        if (addAny) {
            dirty.set(true);
        }
        return addAny;
    }

    @Override
    public boolean retainAll(@Nonnull Collection<?> c) {
        boolean changed = set.retainAll(c);
        if (changed) {
            dirty.set(true);
        }
        return changed;
    }

    @Override
    public boolean removeAll(@Nonnull Collection<?> c) {
        boolean changed = set.removeAll(c);
        if (changed) {
            dirty.set(true);
        }
        return changed;
    }

    @Override
    public void clear() {
        set.clear();
        dirty.set(true);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
